package com.cdeledu.volatile标识数组和对象;

/**以下代码如果是红色标记那一行加volatile关键字，子线程是可以退出循环的，不加的话，子线程没法退出循环，如此说来，volatile变量修饰对象或者数组，当我们改变对象或者数组的成员的时候，岂非不同线程之间具有可见性？
 *
 * 1）代码1中当主线程更改flag字段时候，是调用stop()方法里面的“a.setFlag(false); ”，注意这一句其实包含多步操作，含义丰富：首先是对volatile变量a的读，既然是volatile变量，当然读到的是主存(而不是线程私有的)中的地址,
 * 然后再setFlag来更新这个标志位实际上是更新的主存中引用指向的堆内存；然后是子线程读a.isFlag()，同样的包含多步：首先是对volatile变量的读，当然读的是主存中的引用地址，然后根据这个引用找堆内存中flag值，当然找到的就是
 * 之前主线程写进去的值，所以能够立即生效，子线程退出。

    2）代码1中虽然主线程和子线程都是读volatile值，然后一个是改，一个是读，按照java内存模型中的happen-before，2个线程对volatile变量的读是不具有happen-before特性的，但是这里要注意的是，因为都是以volatile变量为根，层层引用，最后找到的都是同一块堆内存，然后一个修改，一个查看，所以实际上相当于同一个线程在写和修改（因为写和修改的是同一块内存）；所以可以利用happen-before中第一条规则——程序顺序规则，从而有主线程的写happen-before子线程的读
 *
 * @author DELL
 *
 */
public class VolatileObjectTest implements Runnable {
	private volatile ObjectA a;
    public VolatileObjectTest(ObjectA a) {
        this.a = a;
    }

    public ObjectA getA() {
        return a;
    }

    public void setA(ObjectA a) {
        this.a = a;
    }

    @Override
    public void run() {
        long i = 0;
        while (a.isFlag()) {
            i++;
            //加上这句打印，不管ObjectA上加不加volatile都会停止，因为print方法上有synchronized关键字导致停顿，cpu有机会把数据同步到主内存中
            //System.out.println("------------------");
        }
        System.out.println("stop My Thread " + i);
    }

    public void stop() {
        a.setFlag(false);
    }

    public static void main(String[] args) throws InterruptedException {
         // 如果启动的时候加上-server 参数则会 输出 Java HotSpot(TM) Server VM
        System.out.println(System.getProperty("java.vm.name"));

        VolatileObjectTest test = new VolatileObjectTest(new ObjectA());
        new Thread(test).start();

        Thread.sleep(1000);
        System.out.println("停顿");
        test.stop();
        System.out.println("停顿");
        Thread.sleep(1000);
        System.out.println("Main Thread " + test.getA().isFlag());
    }

    static class ObjectA {
        private boolean flag = true;

        public boolean isFlag() {
            return flag;
        }

        public void setFlag(boolean flag) {
            this.flag = flag;
        }

    }
}
